#include "DeflatorSink.h"
#include "log/log.h"

#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
#	include <fcntl.h>
#	include <io.h>
#	define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
#	define SET_BINARY_MODE(file)
#endif


namespace phnq
{
	namespace kit
	{
		namespace compression
		{
			static phnq::kit::log::Logger *log = phnq::kit::log::Logger::get("phnq::kit::compression::DeflatorSink");
			
			DeflatorSink::DeflatorSink(ostream *os, int level, int chunkSize) : boost::iostreams::sink()
			{
				this->os = os;
				this->chunkSize = chunkSize;
				this->out = (unsigned char*)malloc(chunkSize * sizeof(unsigned char));
				
				strm.zalloc = Z_NULL;
				strm.zfree = Z_NULL;
				strm.opaque = Z_NULL;

				ret = deflateInit(&strm, level);

				if(ret != Z_OK)
					log->error("deflateInit: %i", ret);
			}

			streamsize DeflatorSink::write(const char* s, streamsize n)
			{
				strm.avail_in = n;
				strm.next_in = (unsigned char*)s;
				
				do
				{
					strm.avail_out = chunkSize;
					strm.next_out = out;
					
					flush = (n == 0 ? Z_FINISH : Z_NO_FLUSH);
					ret = deflate(&strm, flush);
					
					if(ret == Z_STREAM_ERROR)
						log->error("deflate error: %i", ret);
					
					have = chunkSize - strm.avail_out;
					
					os->write((const char*)out, have);
				} while (strm.avail_out == 0);
				
				return n;
			}
			
			void DeflatorSink::close()
			{
				char s[0];
				write(s, 0);
				
				deflateEnd(&strm);
				free(out);
				out = NULL;
			}
		}
	}
}
